package com.controller;

import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Color;

import com.model.logic.*;
import com.model.objects.*;
import com.view.*;


/**
 * A simple predator-prey simulator, based on a rectangular field
 * containing rabbits and foxes.
 * 
 * @author David J. Barnes and Michael Kölling
 * @version 2011.07.31
 */
public class Simulator implements Runnable
{
    // Constants representing configuration information for the simulation.
    // The default width for the grid.
    private static final int DEFAULT_WIDTH = 180;
    // The default depth of the grid.
    private static final int DEFAULT_DEPTH = 120;
    // The probability that an actor will be created in any given grid position.
    private static double fox_creation_probability = 0.02;
    private static double rabbit_creation_probability = 0.08;  
    private static double crocodile_creation_probability = 0.01;
    private static double jager_creation_probability = 0.002;
	private static double grass_creation_probability = 0.01;
	private static final double LAVA_CREATION_PROBABILITY = 0.0001;
    Color DARK_GREEN = new Color(0x558555);

    // List of Actors in the field.
    public static List<Actor> actors;
    //private HashMap<> classCounter;

    // The current state of the field.
    private Field field;
    //private FieldStats stats;
    // The current nummer of the simulation.
    private int nummer;
    private int simulatieStappen;
    private boolean threadRun;
    private volatile Thread thread;
    
    private boolean run;
    
    // A graphical view of the simulation.
    private SimulatorView view;

    
    /**
     * Construct a simulation field with default size.
     */
    public Simulator()
    {
        this(DEFAULT_DEPTH, DEFAULT_WIDTH);
    }
    public Simulator(SimulatorView view){
    	this(DEFAULT_DEPTH, DEFAULT_WIDTH, view);
    }
    /**
     * Create a simulation field with the given size.
     * @param depth Depth of the field. Must be greater than zero.
     * @param width Width of the field. Must be greater than zero.
     */
    public Simulator(int depth, int width)
    {
        if(width <= 0 || depth <= 0) {
            System.out.println("The dimensions must be greater than zero.");
            System.out.println("Using default values.");
            depth = DEFAULT_DEPTH;
            width = DEFAULT_WIDTH;
            this.threadRun = false;
            this.run = false;
        }
        
        actors = new ArrayList<Actor>();
        field = new Field(depth, width);

        // Create a view of the state of each location in the field.
        view = new SimulatorView(depth, width, this);
        view.setColor(Rabbit.class, Color.ORANGE);
        view.setColor(Fox.class, Color.BLUE);
        view.setColor(Crocodile.class, Color.DARK_GRAY);
        view.setColor(Lava.class, Color.RED);
        view.setColor(Jager.class, Color.YELLOW);
        view.setColor(Grass.class, DARK_GREEN);
        
        // Setup a valid starting point.
        reset();
    }
    public Simulator(int depth, int width, SimulatorView view){
    	if(width <= 0 || depth <= 0) {
            System.out.println("The dimensions must be greater than zero.");
            System.out.println("Using default values.");
            depth = DEFAULT_DEPTH;
            width = DEFAULT_WIDTH;
        }
        
    	actors = new ArrayList<Actor>();
        field = new Field(depth, width);
        this.view = view;
        // Setup a valid starting point.
        reset();
    }
    
    
    /**
     * Run the simulation from its current state for a reasonably long period,
     * (4000 nummers).
     */
    
    public void runLongSimulation()
    {
        simulate(1000);
    }
    
    /**
     * Run the simulation from its current state for the given number of nummers.
     * Stop before the given number of nummers if it ceases to be viable.
     * @param numnummers The number of nummers to run for.
     */
    public void simulate(int numnummers)
    {
    	//stopRun();
    	this.simulatieStappen = numnummers;
    	//thread = null;
    	thread = new Thread(this);
    	thread.start();
    }
    
    public void start(){
    	
    	thread = new Thread(this);
    	thread.start();
    	run = true;
    }
    
    @Override
    public void run()
    {
    	if(threadRun == true){
    		return;
    	}
    	do {
    		simulateOneStep();
    		
    		if(simulatieStappen > 0){
    			simulatieStappen--;
    		}
    			
		    try{
		    	Thread.sleep(10);
		    } catch (Exception e){}
	    
    	}
    	    while(simulatieStappen>0 || run == true && view.isViable(field));
    	

    }
    
    public void stopRun(){
    	
    	run = false;
    	simulatieStappen = 0;
    	
    }
    
    /**
     * Run the simulation from its current state for a single nummer.
     * Iterate over the whole field updating the state of each
     * fox and rabbit.
     */
    public void simulateOneStep()
    {
        nummer++;
        
        // Provide space for newborn actors.
        List<Actor> newActors = new ArrayList<Actor>();
        
        // avoid being being interrupted
        
	        	if (actors != null)
	        	{

				    // Let all actors act.
				    for(Iterator<Actor> it = actors.iterator(); it.hasNext();) {
				        Actor actor = it.next();
				        actor.act(newActors);	        		
				        
				        if (actor instanceof Animal)		//	check if actor is an animal
				        {
				        	Animal animal = (Animal) actor;
				        	if(! animal.isAlive()) {
				                it.remove();
				            }
				        }
				        else if (actor instanceof Plant)
				        {
				        	Plant plant = (Plant) actor;
				        	if(! plant.isAlive()) {
				                it.remove();
				            }
				        }
				        
				    }
	        	}
        	
        // Add the newly born foxes and rabbits to the main lists.
	    
        actors.addAll(newActors);

        view.showStatus(nummer, field);
    }
        
    /**
     * Reset the simulation to a starting position.
     */
    public void reset()
    {
    	if(!threadRun){
    		stopRun();
    		nummer = 0;
    		actors.clear();
    		populate();
    	}
	    try{
	    	Thread.sleep(20);
    		stopRun();
    		nummer = 0;
    		actors.clear();
    		populate();
	    } catch (Exception e){}
      
        // Show the starting state in the view.
        view.showStatus(nummer, field);
    }
    
    /**
     * Randomly populate the field with foxes and rabbits.
     */
    private void populate()
    {    
    	
        Random rand = Randomizer.getRandom();
        field.clear();
      //  stats.updatePopulationDetails(field);
        for(int row = 0; row < field.getDepth(); row++) {
            for(int col = 0; col < field.getWidth(); col++) {
                if(rand.nextDouble() <= fox_creation_probability) {
                    Location location = new Location(row, col);
                    Fox fox = new Fox(true, field, location);
                    actors.add(fox);
                }
                else if(rand.nextDouble() <= rabbit_creation_probability) {
                    Location location = new Location(row, col);
                    Rabbit rabbit = new Rabbit(true, field, location);
                    actors.add(rabbit);
                }
                else if(rand.nextDouble() <= crocodile_creation_probability) {
                    Location location = new Location(row, col);
                    Crocodile crocodile = new Crocodile(true, field, location);
                    actors.add(crocodile);
                }
                else if(rand.nextDouble() <= jager_creation_probability) {
                    Location location = new Location(row, col);
                    Jager jager = new Jager(field, location);
                    actors.add(jager);
                }

                else if(rand.nextDouble() <= grass_creation_probability) {
                    Location location = new Location(row, col);
                    Grass grass = new Grass(field, location);
                    actors.add(grass);
            }  
        }
        }
    } 
    
	public void startLava(){
		
        MP3 mp3 = new MP3("lava.mp3");
        mp3.play();
		
		Random rand = Randomizer.getRandom();
        for(int row = 0; row < field.getDepth(); row++) {
            for(int col = 0; col < field.getWidth(); col++) {
            	if(rand.nextDouble() <= LAVA_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Lava lava = new Lava(location, field);
                    actors.add(lava);
                }
            }
        }
	}
	
    public int getCurrentnummer(){
    	return nummer;
    }
    
    /**
     * setter voor wolf_creation_probability
     * @param wolf_creation_probability
     */
    public static void setCrocodileCreationProbability(double crocodile_creation_probability)
    {
    	if (crocodile_creation_probability >= 0)
    		Simulator.crocodile_creation_probability = crocodile_creation_probability;
    }
    
    /**
     * getter voor crocodile_creation_probability
     */
    public static String getCrocodileCreationProbability()
    {
    	return "currently at " + Double.toString(Simulator.crocodile_creation_probability);
    }
    
    /**
     * setter voor fox_creation_probability
     * @param fox_creation_probability
     */
    public static void setFoxCreationProbability(double fox_creation_probability)
    {
    	if (fox_creation_probability >= 0)
    		Simulator.fox_creation_probability = fox_creation_probability;
    }
    
    /**
     * getter voor fox_creation_probability
     */
    public static String getFoxCreationProbability()
    {
    	return "currently at " + Double.toString(Simulator.fox_creation_probability);
    }
    
    /**
     * setter voor rabbit_creation_probability
     * @param rabbit_creation_probability
     */
    public static void setRabbitCreationProbability(double rabbit_creation_probability)
    {
    	if (rabbit_creation_probability >= 0)
    		Simulator.rabbit_creation_probability = rabbit_creation_probability;
    }
    
    /**
     * getter voor rabbit_creation_probability
     */
    public static String getRabbitCreationProbability()
    {
    	return "currently at " + Double.toString(Simulator.rabbit_creation_probability);
    }
    
    /**
     * setter voor hunter_creation_probability
     * @param hunter_creation_probability
     */
    public static void setJagerCreationProbability(double hunter_creation_probability)
    {
    	if (hunter_creation_probability >= 0)
    		Simulator.jager_creation_probability = hunter_creation_probability;
    }
    
    /**
     * getter voor jager_creation_probability
     */
    public static String getJagerCreationProbability()
    {
    	return "currently at " + Double.toString(Simulator.jager_creation_probability);
    }
    
    public static void setGrassCreationProbability(double grass_creation_probability)
    {
    	if (grass_creation_probability >= 0)
    		Simulator.grass_creation_probability = grass_creation_probability;
    }    
    
    /**
     * default settings
     */
    public static void setDefault()
    {
        fox_creation_probability = 0.09;
        rabbit_creation_probability = 0.3;  
    	crocodile_creation_probability = 0.020;
        jager_creation_probability = 0.05;
        grass_creation_probability = 0.1;
    }  	
}
